<template>
  <div class="page-wrapper">
    <!-- 引入字体组件 -->
    <font-loader />

    <!-- 页面禁用遮罩：当必需参数缺失时阻止交互并提示 -->
    <div v-if="pageDisabled" class="page-disabled-mask">
      <div class="mask-content">
        <i class="el-icon-warning-outline mask-icon"></i>
        <div class="mask-title">缺少必要参数</div>
        <div class="mask-desc">未获取到 App名称，请通过带有 appName 参数的链接进入本页。</div>
      </div>
    </div>
    <!-- 顶部导航栏 -->
    <nav-bar :is-loggedin="false" @login="handleLogin" />

    <div class="home-root">
      <!-- 主内容区 -->
      <main class="main-container">
      <!-- 左侧历史会话列表 -->
      <aside class="left-panel">
        <div class="panel-section">
          <h3 class="section-title">
            <i class="fas fa-history"></i>历史会话
          </h3>
          <div class="services-grid">
            <button class="service-item new-chat-btn" @click="startNewChat">
              <i class="fas fa-plus"></i>
              <span>新建对话</span>
            </button>
            <div
              v-for="conv in conversations"
              :key="conv.id"
              class="service-item"
              :class="{ active: currentConversationId === conv.id }"
              @click="loadConversation(conv.id)"
            >
              <i class="fas fa-comment"></i>
              <span class="conversation-title">{{ conv.name || '新对话' }}</span>
              <button
                class="delete-conv-btn"
                @click.stop="deleteChat(conv.id)"
                title="删除"
              >
                <i class="fas fa-times"></i>
              </button>
            </div>
          </div>
        </div>
      </aside>

      <!-- 聊天窗口 -->
      <section class="chat-container">
        <div class="chat-messages" ref="chatMessages">
          <!-- 欢迎消息 -->
          <div v-if="messages.length === 0" class="message">
            <div class="header-bubble">
              <div class="bubble-container">
                <div class="gif-container">
                  <img src="/img/boy1.gif" alt="卡通人物" />
                </div>
                <div class="welcome-content">
                  <div class="welcome-title">
                    Hi！我是桂小库智能助手，很高兴为您服务~
                  </div>
                  <div class="welcome-txt">
                    我专注于提供国库领域的专业咨询服务，以及国库行政许可业务办理情况相关数据查询，我会以图表、报告等容易理解的方式回复您。请问您需要了解哪方面的国库政策或业务问题，我将为您提供相关解答以供参考。
                  </div>
                  <div class="suggestions-title">
                    <i class="fas fa-lightbulb"></i>
                    您可以这样问一问~
                  </div>
                  <div class="suggestions">
                                    <div class="suggestion">
                                        <i class="fas fa-passport"></i>
                                        <div>国库行政许可业务办理流程是怎么样的？</div>
                                    </div>
                                    <div class="suggestion">
                                        <i class="fas fa-briefcase"></i>
                                        <div>2025年共办理了多少项国库行政许可业务？</div>
                                    </div>
                                    <!--
                                    <div class="suggestion">
                                        <i class="fas fa-id-card"></i>
                                        <div>个人移民财产转移？</div>
                                    </div>-->
                                </div>
                  <!--
                  <div class="suggestions">
                    <div
                      v-for="(question, idx) in suggestedQuestions"
                      :key="idx"
                      class="suggestion"
                      @click="setQuickMessage(question)"
                    >
                      <i :class="getSuggestionIcon(idx)"></i>
                      <div>{{ question }}</div>
                    </div>
                  </div>
                -->
                </div>
              </div>
            </div>
          </div>

          <!-- 消息列表 -->
          <div
            v-for="(msg, idx) in messages"
            :key="msg.messageId || `${msg.role}-${idx}`"
            class="message"
            :class="msg.role === 'user' ? 'user-message' : ''"
          >
            <!-- AI消息 -->
            <template v-if="msg.role === 'assistant'">
              <div class="ai-avatar-small">
                <img src="/img/logo1.png" alt="logo" />
              </div>
              <div class="message-bubble ai-bubble">
                <div class="message-text">
                  <!-- 思考过程：一旦锁定就不再重新渲染 -->
                  <div v-if="msg.thinkingLocked && msg.thinkingContent" v-html="formatThinkingBlock(msg.thinkingContent, msg.messageId)" class="thinking-section"></div>

                  <!-- 回答内容：流式更新（使用打字效果显示） -->
                  <div v-if="msg.displayedAnswerContent || msg.answerContent" v-html="formatAnswerContent(msg.displayedAnswerContent || msg.answerContent)" class="answer-section"></div>

                  <!-- 兼容旧格式：如果没有分离字段，使用原方法 -->
                  <div v-if="!msg.thinkingLocked && !msg.answerContent && msg.content" v-html="formatMessage(msg.content)"></div>
                </div>
              </div>
            </template>

            <!-- 用户消息 -->
            <template v-else>
              <div class="message-bubble user-bubble">
                <div class="message-content">
                  <p>{{ msg.content }}</p>
                  <!-- 显示上传的文件 -->
                  <div v-if="msg.uploadedFiles && msg.uploadedFiles.length > 0" class="uploaded-files">
                    <div
                      v-for="(file, idx) in msg.uploadedFiles"
                      :key="idx"
                      class="uploaded-file-item"
                    >
                      <i class="fas fa-file"></i>
                      <span>{{ file.name }}</span>
                    </div>
                  </div>
                </div>
              </div>
              <div class="message-avatar user-avatar-small">
                <span class="user-avatar-text">{{ getUserNameSecondChar }}</span>
              </div>
            </template>
          </div>

          <!-- 加载中提示：仅在还没有收到任何AI回答时显示 -->
          <div v-if="isLoading && !hasAnyAssistantContent" class="message">
            <div class="ai-avatar-small">
              <img src="/img/logo.png" alt="logo" />
            </div>
            <div class="message-bubble ai-bubble">
              <div class="typing-indicator">
                <span>正在思考</span>
                <div class="typing-dots">
                  <span class="dot"></span>
                  <span class="dot"></span>
                  <span class="dot"></span>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- 输入区域 -->
        <div class="input-area">
          <!-- 文件列表 -->
          <div v-if="attachments.length > 0" class="attachments-list">
            <div
              v-for="(file, idx) in attachments"
              :key="idx"
              class="attachment-item"
            >
              <i class="fas fa-file"></i>
              <span>{{ file.name }}</span>
              <button @click="removeAttachment(idx)" class="remove-file-btn">
                <i class="fas fa-times"></i>
              </button>
            </div>
          </div>

          <!-- 输入框 -->
          <div class="input-box">
            <input
              ref="fileInput"
              type="file"
              multiple
              class="hidden-file"
              @change="handleFileSelect"
            />
            <textarea
              v-model="message"
              ref="textarea"
              placeholder="请简要描述您的问题，避免出现具体事例和个人信息"
              @keydown="handleKeydown"
              :disabled="isLoading"
            ></textarea>
            <button class="upload-btn" @click="triggerFileUpload" title="上传文件">
              <i class="fas fa-paperclip"></i>
            </button>
            <button class="send-btn" @click="sendMessage" :disabled="isLoading || !message.trim()">
              <i class="fas fa-paper-plane"></i>
            </button>
          </div>
        </div>
      </section>
      </main>
    </div>
  </div>
</template>

<script>
import NavBar from "@/layout/UserView/NavBar.vue";
import FontLoader from "@/components/UserView/general/font.vue";
import {
  sendChatMessage,
  uploadFile,
  getConversations,
  deleteConversation,
  getMessages,
} from "@/api/dify/chat";
import { getCurrentUser } from "@/utils/userService";
import * as echarts from "echarts";

export default {
  name: "Hui2Chat",
  components: {
    NavBar,
    FontLoader,
  },
  data() {
    return {
      message: "",
      messages: [],
      attachments: [],
      conversations: [],
      currentConversationId: null,
      isLoading: false,
      userId: "",
      userName: "",
      appName: null,
      pageDisabled: false,
      currentTaskId: null,
      abortController: null,
      streamController: null,
      suggestedQuestions: [
        "个人便利化购汇额度？",
        "外贸企业名录登记？",
        "个人移民财产转移？",
      ],
      // echarts 实例存储
      echartsInstances: new Map(),
    };
  },
  computed: {
    // 获取用户名称的后两个字
    getUserNameSecondChar() {
      if (!this.userName || this.userName.length === 0) {
        return "用户";
      }
      if (this.userName.length === 1) {
        return this.userName;
      }
      // 返回后两个字
      return this.userName.slice(-2);
    },
    // 检查是否已收到任何AI回答
    hasAnyAssistantContent() {
      return this.messages.some(
        (msg) =>
          msg.role === "assistant" &&
          (msg.content || msg.answerContent || msg.thinkingContent)
      );
    },
  },
  async mounted() {
    // 动态引入 Font Awesome CSS
    await this.loadFontAwesome();

    // 全局函数：切换思考过程显示/隐藏
    window.toggleThinkBlock = function (thinkId) {
      const contentEl = document.getElementById(`content-${thinkId}`);
      const iconEl = document.getElementById(`icon-${thinkId}`);
      if (contentEl && iconEl) {
        const isCollapsed = contentEl.classList.contains("collapsed");
        if (isCollapsed) {
          contentEl.classList.remove("collapsed");
          iconEl.classList.remove("el-icon-arrow-down");
          iconEl.classList.add("el-icon-arrow-up");
        } else {
          contentEl.classList.add("collapsed");
          iconEl.classList.remove("el-icon-arrow-up");
          iconEl.classList.add("el-icon-arrow-down");
        }
      }
    };

    // 全局函数：下载文件
    window.downloadFile = async function (url, fileName) {
      try {
        // 如果 URL 是相对路径，添加 baseURL
        let downloadUrl = url;
        if (url && url.startsWith('/files/')) {
          const baseURL = process.env.VUE_APP_BASE_API || '/dev-api';
          downloadUrl = baseURL + url;
        }
        
        // 尝试使用 fetch + blob 方式下载（支持添加 token）
        try {
          // 获取 token（从 Cookie 中获取）
          function getCookie(name) {
            const value = `; ${document.cookie}`;
            const parts = value.split(`; ${name}=`);
            if (parts.length === 2) return parts.pop().split(';').shift();
            return null;
          }
          const token = getCookie('vue_admin_template_token');
          
          const response = await fetch(downloadUrl, {
            method: 'GET',
            headers: token ? {
              'token': token
            } : {}
          });
          
          if (!response.ok) {
            throw new Error(`下载失败: ${response.status} ${response.statusText}`);
          }
          
          // 获取文件 blob
          const blob = await response.blob();
          
          // 创建下载链接
          const blobUrl = window.URL.createObjectURL(blob);
          const link = document.createElement('a');
          link.href = blobUrl;
          link.download = fileName || 'download';
          link.style.display = 'none';
          
          // 添加到 DOM 并触发点击
          document.body.appendChild(link);
          link.click();
          
          // 清理
          setTimeout(() => {
            document.body.removeChild(link);
            window.URL.revokeObjectURL(blobUrl);
          }, 100);
        } catch (fetchError) {
          console.warn('使用 fetch 下载失败，尝试直接下载:', fetchError);
          // 如果 fetch 失败，尝试直接下载
          const link = document.createElement('a');
          link.href = downloadUrl;
          link.download = fileName || 'download';
          link.target = '_blank';
          link.style.display = 'none';
          
          document.body.appendChild(link);
          link.click();
          
          setTimeout(() => {
            document.body.removeChild(link);
          }, 100);
        }
      } catch (error) {
        console.error('文件下载失败:', error);
        // 如果下载失败，尝试在新窗口打开
        let downloadUrl = url;
        if (url && url.startsWith('/files/')) {
          const baseURL = process.env.VUE_APP_BASE_API || '/dev-api';
          downloadUrl = baseURL + url;
        }
        window.open(downloadUrl, '_blank');
      }
    };

    // 从路由参数获取 App名称
    if (this.$route.query.appName) {
      this.appName = this.$route.query.appName;
      console.log("📝 从路由参数获取到 App名称:", this.appName);
    }

    // 初始化用户ID
    await this.initializeUserId();

    // 加载会话列表
    await this.loadConversationsList();
  },
  updated() {
    // 在 DOM 更新后初始化所有 echarts 图表
    this.$nextTick(() => {
      try {
        if (this.initAllEchartsCharts && typeof this.initAllEchartsCharts === 'function') {
          // 延迟一点时间，确保 DOM 完全渲染和尺寸计算完成
          setTimeout(() => {
            this.initAllEchartsCharts();
          }, 50);
        }
      } catch (error) {
        console.warn('初始化 echarts 图表时出错:', error);
      }
    });
  },
  beforeDestroy() {
    // 清理所有 echarts 实例
    this.echartsInstances.forEach((chart) => {
      if (chart) {
        // 移除 resize 事件监听器
        if (chart._resizeHandler) {
          window.removeEventListener("resize", chart._resizeHandler);
        }
        // 销毁图表实例
        if (typeof chart.dispose === "function") {
          chart.dispose();
        }
      }
    });
    this.echartsInstances.clear();
  },
  methods: {
    // 加载 Font Awesome（使用本地文件）
    loadFontAwesome() {
      return new Promise((resolve) => {
        // 检查是否已经加载
        if (document.querySelector('link[href*="fontawesome"]')) {
          resolve();
          return;
        }

        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = '/fontawesome/css/all.min.css';
        
        // 监听加载完成
        link.onload = () => {
          console.log('Font Awesome CSS 加载完成（本地文件）');
          resolve();
        };
        link.onerror = () => {
          console.error('本地 Font Awesome CSS 加载失败，尝试 CDN 备用方案');
          // 备用方案：使用 CDN
          const fallbackLink = document.createElement('link');
          fallbackLink.rel = 'stylesheet';
          fallbackLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css';
          fallbackLink.crossOrigin = 'anonymous';
          fallbackLink.onload = () => {
            console.log('Font Awesome CSS 加载完成（CDN 备用）');
            resolve();
          };
          fallbackLink.onerror = () => {
            console.error('CDN 备用方案也加载失败');
            resolve(); // 即使失败也继续，避免阻塞
          };
          document.head.appendChild(fallbackLink);
        };
        
        document.head.appendChild(link);
      });
    },

    // 初始化用户ID
    async initializeUserId() {
      try {
        const userInfo = await getCurrentUser();
        if (userInfo && userInfo.loginName) {
          this.userId = userInfo.loginName;
        } else if (userInfo && userInfo.userSn) {
          this.userId = userInfo.userSn;
        } else {
          this.userId = "user_" + Date.now();
        }
        // 获取用户名称
        if (userInfo && userInfo.userName) {
          this.userName = userInfo.userName;
        } else if (userInfo && userInfo.name) {
          this.userName = userInfo.name;
        } else if (userInfo && userInfo.loginName) {
          this.userName = userInfo.loginName;
        } else {
          this.userName = "用户";
        }
        console.log("👤 用户ID:", this.userId);
        console.log("👤 用户名称:", this.userName);
      } catch (error) {
        console.error("获取用户信息失败:", error);
        this.userId = "user_" + Date.now();
        this.userName = "用户";
      }
    },

    // 加载会话列表
    async loadConversationsList() {
      try {
        const result = await getConversations(this.userId, 50, this.appName);
        if (result.success) {
          this.conversations = result.data || [];
        } else {
          console.error("加载会话列表失败:", result.error);
        }
      } catch (error) {
        console.error("加载会话列表失败:", error);
      }
    },

    // 加载会话历史
    async loadConversation(conversationId) {
      if (this.isLoading) return;

      this.currentConversationId = conversationId;
      this.isLoading = true;

      try {
        const result = await getMessages(
          conversationId,
          this.userId,
          50,
          this.appName
        );

        if (result.success && result.data) {
          this.messages = [];
          result.data.forEach((msg) => {
            // 用户消息
            if (msg.query) {
              this.messages.push({
                role: "user",
                content: msg.query,
                messageId: `user-${msg.id}`,
                created_at: msg.created_at,
              });
            }

            // AI回复
            if (msg.answer) {
              this.messages.push({
                role: "assistant",
                content: msg.answer,
                messageId: `assistant-${msg.id}`,
                created_at: msg.created_at,
              });
            }
          });

          this.$nextTick(() => {
            this.scrollToBottom();
          });
        }
      } catch (error) {
        console.error("加载会话历史失败:", error);
        this.$message.error("加载会话历史失败");
      } finally {
        this.isLoading = false;
      }
    },

    // 开始新对话
    startNewChat() {
      this.currentConversationId = null;
      this.messages = [];
      this.attachments = [];
      this.message = "";
    },

    // 删除会话
    async deleteChat(conversationId) {
      try {
        await this.$confirm("确定要删除这个会话吗？", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        });

        await deleteConversation(conversationId, this.userId, this.appName);
        await this.loadConversationsList();

        if (this.currentConversationId === conversationId) {
          this.startNewChat();
        }

        this.$message.success("删除成功");
      } catch (error) {
        if (error !== "cancel") {
          console.error("删除会话失败:", error);
          this.$message.error("删除会话失败");
        }
      }
    },

    // 发送消息
    async sendMessage() {
      if (!this.message.trim() || this.isLoading) return;

      const userMessage = this.message.trim();
      this.message = "";

      // 保存附件信息到临时变量（用于上传）
      const attachmentsToUpload = [...this.attachments];

      // 保存上传的文件信息（用于显示和预览）
      const uploadedFilesInfo = attachmentsToUpload.length > 0
        ? attachmentsToUpload.map((f) => ({
            name: f.name,
            size: f.size,
            raw: f,
            url: f ? URL.createObjectURL(f) : null,
          }))
        : [];

      // 立即清空附件列表（隐藏文件列表）
      this.attachments = [];

      // 添加用户消息到界面
      this.messages.push({
        role: "user",
        content: userMessage,
        messageId: `user-${Date.now()}`,
        created_at: Date.now(),
        uploadedFiles: uploadedFilesInfo, // 保存文件信息用于显示
      });

      this.scrollToBottom();
      this.isLoading = true;

      // 上传文件
      let uploadedFiles = [];
      if (attachmentsToUpload.length > 0) {
        try {
          for (const file of attachmentsToUpload) {
            console.log("开始处理文件上传:", file.name);
            const uploadResult = await uploadFile(file, this.userId, this.appName);
            console.log("uploadResult:", uploadResult);
            const difyFileId = this.extractDifyFileId(uploadResult);
            console.log("difyFileId:", difyFileId);
            if (!difyFileId) {
              throw new Error("无法获取文件 ID");
            }
            uploadedFiles.push({
              type: this.getFileType(file.name),
              transfer_method: "local_file",
              upload_file_id: difyFileId,
            });
          }
          console.log("📤 已上传 " + attachmentsToUpload.length + " 个文件");
          console.log("📤 files 参数格式:", JSON.stringify(uploadedFiles, null, 2));
        } catch (error) {
          console.error("文件上传失败:", error);
          this.$message.error("文件上传失败：" + (error.message || "未知错误"));
          this.isLoading = false;
          return;
        }
      }

      // 创建AI消息占位符
      const currentTime = Date.now();
      let aiMessageIndex = this.messages.length;
      this.messages.push({
        role: "assistant",
        content: "",
        thinkingContent: "", // 🔥 思考过程内容（一旦完成就不再变化）
        answerContent: "", // 🔥 回答内容（流式更新）
        displayedAnswerContent: "", // 🔥 显示的回答内容（用于打字效果）
        thinkingLocked: false, // 🔥 思考过程是否已锁定
        typingTimer: null, // 🔥 打字效果定时器
        created_at: currentTime,
        messageId: `assistant-${currentTime}`,
        partialAnswer: "", // 🔥 临时存储流式接收的完整答案
      });
      this.scrollToBottom();

      // 创建 AbortController
      this.abortController = new AbortController();

      // 临时存储当前消息的思考内容
      let currentThoughts = [];
      let currentFiles = [];

      // 发送消息到后端
      try {
        const requestParams = {
          query: userMessage,
          user: this.userId,
          conversation_id: this.currentConversationId,
          signal: this.abortController.signal,
          appName: this.appName,
        };

        // 处理 files
        if (uploadedFiles.length > 0) {
          requestParams.files = uploadedFiles;
        }

        this.streamController = sendChatMessage(
          requestParams,
          // onMessage
          (data) => {
            // 🔥 添加调试日志
            console.log('🔔 收到流式数据:', {
              event: data.event,
              answerLength: data.answer?.length || 0,
              answerPreview: data.answer ? data.answer.substring(0, 50) + '...' : 'null',
              conversationId: data.conversation_id
            })

            // 保存 task_id
            if (data.task_id && !this.currentTaskId) {
              this.currentTaskId = data.task_id;
            }

            if (!this.messages[aiMessageIndex]) return;

            // 根据事件类型处理
            switch (data.event) {
              case "agent_thought":
                // Agent 思考事件
                if (data.thought) {
                  const existingThought = currentThoughts.find(
                    (t) => t.id === data.id
                  );
                  if (!existingThought) {
                    currentThoughts.push({
                      id: data.id,
                      thought: data.thought,
                      tool: data.tool || "",
                      observation: data.observation || "",
                      position: data.position || currentThoughts.length + 1,
                    });
                  } else {
                    // 更新已有的思考
                    Object.assign(existingThought, {
                      thought: data.thought,
                      tool: data.tool || existingThought.tool,
                      observation:
                        data.observation || existingThought.observation,
                    });
                  }
                }
                break;

              case "message_file":
                // 文件事件
                if (data.file) {
                  const existingFile = currentFiles.find(
                    (f) => f.id === data.file.id
                  );
                  if (!existingFile) {
                    currentFiles.push(data.file);
                  }
                }
                break;

              case "message":
              case "agent_message":
                // 常规消息事件 - answer 是增量的
                if (data.answer) {
                  // 将新的内容追加到消息中（但要重新构建完整内容）
                  this.messages[aiMessageIndex].partialAnswer =
                    (this.messages[aiMessageIndex].partialAnswer || "") +
                    data.answer;
                }
                break;
            }

            // 🔥 新的流式更新策略：分离思考和回答，避免闪烁
            const rawAnswer = this.messages[aiMessageIndex].partialAnswer || "";
            const msg = this.messages[aiMessageIndex];

            // 检查是否包含 </think> 结束标签
            const hasEndTag = rawAnswer.includes("</think>");

            if (hasEndTag && !msg.thinkingLocked) {
              // 第一次检测到 </think>，提取并锁定思考过程
              const parts = rawAnswer.split("</think>");
              let thinkContent = parts[0].replace(/^<think>/i, "").trim();

              // 移除所有可能残留的标签
              if (thinkContent) {
                thinkContent = this.cleanThinkingTags(thinkContent);
                
                console.log("🔒 锁定思考过程，长度:", thinkContent.length);
                msg.thinkingContent = thinkContent;
                msg.thinkingLocked = true;
              }

              // 剩余部分是回答内容
              const remainingAnswer = parts
                .slice(1)
                .join("")
                .trim()
                .replace(/^\s*\n+/, "");
              msg.answerContent = remainingAnswer;
              // 🔥 触发打字效果
              this.startTypingEffect(aiMessageIndex, remainingAnswer);
            } else if (msg.thinkingLocked) {
              // 思考过程已锁定，只更新回答内容
              // 需要去掉 rawAnswer 中已经处理过的思考部分
              const cleanAnswer = rawAnswer
                .split("</think>")
                .slice(1)
                .join("")
                .trim()
                .replace(/^\s*\n+/, "");
              msg.answerContent = cleanAnswer;
              // 🔥 触发打字效果
              this.startTypingEffect(aiMessageIndex, cleanAnswer);
            } else {
              // 还没有遇到 </think>，暂时不处理
              // 这种情况下思考过程还在流式接收中
              msg.answerContent = rawAnswer;
              // 🔥 触发打字效果
              this.startTypingEffect(aiMessageIndex, rawAnswer);
            }

            // 构建完整内容用于兼容性（某些地方可能还在用 content）
            let fullContent = "";
            if (msg.thinkingContent && msg.thinkingLocked) {
              fullContent = `<think>${msg.thinkingContent}</think>`;
            }
            if (msg.answerContent) {
              fullContent += msg.answerContent;
            }

            // 3. 添加文件
            if (currentFiles.length > 0) {
              fullContent += "\n\n";
              currentFiles.forEach((file) => {
                if (file.type === "image") {
                  fullContent += `\n<img src="${file.url}" alt="生成的图片" style="max-width: 100%; border-radius: 8px; margin-top: 8px;" />`;
                } else {
                  // 尝试多种方式获取文件名
                  let fileName = file.name || file.filename || file.title;

                  // 尝试从 belongs_to 字段获取
                  if (!fileName && file.belongs_to) {
                    fileName = file.belongs_to.name || file.belongs_to.filename;
                  }

                  // 尝试从 extension_data 获取
                  if (!fileName && file.extension_data) {
                    fileName =
                      file.extension_data.name || file.extension_data.filename;
                  }

                  // 尝试从 URL 查询参数中获取
                  if (!fileName && file.url) {
                    try {
                      const urlObj = new URL(file.url);
                      // 检查查询参数
                      const nameParam =
                        urlObj.searchParams.get("name") ||
                        urlObj.searchParams.get("filename") ||
                        urlObj.searchParams.get("file_name");
                      if (nameParam) {
                        fileName = decodeURIComponent(nameParam);
                      } else {
                        // 尝试从路径提取
                        const pathParts = urlObj.pathname.split("/");
                        const lastPart = pathParts[pathParts.length - 1];
                        if (lastPart && lastPart !== "file-preview") {
                          fileName = decodeURIComponent(lastPart);
                        }
                      }
                    } catch (e) {
                      console.warn("无法从 URL 提取文件名:", e);
                    }
                  }

                  // 最后的回退选项
                  if (!fileName || fileName === "file-preview") {
                    fileName = `${file.type || "document"}文件`;
                  }

                  fullContent += `\n<div class="file-link-wrapper"><i class="el-icon-document"></i> <a href="${file.url}" target="_blank" class="file-link">${fileName}</a></div>`;
                }
              });
            }

            // 更新消息内容
            this.messages[aiMessageIndex].content = fullContent;

            // 🔥 保持 isLoading = true 直到流式结束，确保停止按钮可用
            // 不再过早关闭加载状态，让用户可以随时终止

            this.$nextTick(() => {
              this.scrollToBottom();
            });
          },
          // onEnd
          (data) => {
            this.isLoading = false;
            this.currentTaskId = null;
            this.abortController = null;
            this.streamController = null;
            this.attachments = [];

            // 🔥 确保打字效果完成，显示所有内容
            if (this.messages[aiMessageIndex]) {
              const msg = this.messages[aiMessageIndex];
              // 清除定时器
              if (msg.typingTimer) {
                clearInterval(msg.typingTimer);
                msg.typingTimer = null;
              }
              // 确保显示内容等于完整内容
              if (msg.answerContent) {
                msg.displayedAnswerContent = msg.answerContent;
              }
            }

            // 重置临时变量
            currentThoughts = [];
            currentFiles = [];

            // 保存 conversation_id
            if (data.conversation_id && !this.currentConversationId) {
              this.currentConversationId = data.conversation_id;
              this.loadConversationsList();
            }

            // 处理引用和归属
            if (
              data.metadata?.retriever_resources &&
              data.metadata.retriever_resources.length > 0
            ) {
              const retrieverResources = data.metadata.retriever_resources;
              let referencesHtml = '\n\n<div class="references-section">';
              referencesHtml +=
                '<div class="references-title"><i class="el-icon-link"></i> 引用和归属</div>';
              retrieverResources.forEach((resource, idx) => {
                referencesHtml += `<div class="reference-item">`;
                referencesHtml += `<div class="reference-header">`;
                referencesHtml += `<span class="reference-index">${
                  idx + 1
                }</span>`;
                referencesHtml += `<span class="reference-dataset">${resource.dataset_name}</span>`;
                referencesHtml += `<span class="reference-score">相似度: ${(
                  resource.score * 100
                ).toFixed(1)}%</span>`;
                referencesHtml += `</div>`;
                referencesHtml += `<div class="reference-content">${
                  resource.content?.substring(0, 200) || ""
                }...</div>`;
                referencesHtml += `</div>`;
              });
              referencesHtml += "</div>";

              // 添加到最后一条 AI 消息
              if (this.messages[aiMessageIndex]) {
                this.messages[aiMessageIndex].content += referencesHtml;
                this.messages[aiMessageIndex].hasReferences = true;
              }
            }

            this.$nextTick(() => {
              this.scrollToBottom();
            });
          },
          // onError
          (error) => {
            this.isLoading = false;
            this.currentTaskId = null;
            this.abortController = null;
            this.streamController = null;

            // 🔥 清除打字效果定时器
            if (this.messages[aiMessageIndex]) {
              const msg = this.messages[aiMessageIndex];
              if (msg.typingTimer) {
                clearInterval(msg.typingTimer);
                msg.typingTimer = null;
              }
              // 确保显示内容等于完整内容
              if (msg.answerContent) {
                msg.displayedAnswerContent = msg.answerContent;
              }
            }

            // 重置临时变量
            currentThoughts = [];
            currentFiles = [];

            // 如果是 AbortError，说明是用户主动停止，不显示错误消息
            if (error.name !== "AbortError") {
              this.$message.error("发送失败：" + (error.message || "未知错误"));
              // 移除失败的AI消息
              const index = this.messages.findIndex(m => m.messageId === this.messages[aiMessageIndex]?.messageId);
              if (index > -1) {
                this.messages.splice(index, 1);
              }
            } else {
              console.log("✅ 用户已停止生成");
            }
          }
        );
      } catch (error) {
        this.isLoading = false;
        console.error("发送消息失败:", error);
        this.$message.error("发送消息失败");
        // 移除失败的AI消息
        const index = this.messages.indexOf(assistantMessage);
        if (index > -1) {
          this.messages.splice(index, 1);
        }
      }
    },

    // 设置快速消息
    setQuickMessage(question) {
      this.message = question;
      this.$nextTick(() => {
        this.sendMessage();
      });
    },

    /**
     * 清理思考内容中的标签
     * @param {string} content - 原始内容
     * @returns {string} 清理后的内容
     */
    cleanThinkingTags(content) {
      if (!content) return "";
      // 移除所有可能的 think 标签变体
      return content
        .replace(/<think>/gi, "")
        .replace(/<\/think>/gi, "")
        .replace(/<think>/gi, "")
        .replace(/<\/redacted_reasoning>/gi, "")
        .replace(/<think>/gi, "")
        .replace(/<\/think>/gi, "")
        .trim();
    },

    /**
     * 🔥 格式化思考过程块（一旦锁定就不再变化，避免闪烁）
     * @param {string} thinkingContent - 思考内容
     * @param {string} messageId - 消息ID，用于生成稳定的块ID
     */
    formatThinkingBlock(thinkingContent, messageId) {
      if (!thinkingContent) return "";

      // 使用消息ID生成稳定的思考块ID
      const thinkId = `think-${messageId}`;

      // 移除可能残留的标签（双重保险，确保标签被完全移除）
      let cleanContent = this.cleanThinkingTags(thinkingContent);

      // 格式化思考内容（传入 type="think" 确保标签被清理）
      const formatted = this.formatTextContent(cleanContent, "think", null);

      return `<div class="think-block">
        <div class="think-header" onclick="toggleThinkBlock('${thinkId}')">
          <i class="el-icon-view"></i>
          <span>思考过程</span>
          <i class="el-icon-arrow-up think-toggle-icon" id="icon-${thinkId}"></i>
        </div>
        <div class="think-content" id="content-${thinkId}">${formatted}</div>
      </div>`;
    },

    /**
     * 🔥 格式化回答内容（流式更新，不包含思考过程）
     * @param {string} answerContent - 回答内容
     */
    formatAnswerContent(answerContent) {
      if (!answerContent) return "";
      return this.formatTextContent(answerContent, "main", null);
    },

    /**
     * 🔥 打字效果：逐字显示内容
     * @param {number} messageIndex - 消息索引
     * @param {string} targetContent - 目标内容
     */
    startTypingEffect(messageIndex, targetContent) {
      const msg = this.messages[messageIndex];
      if (!msg) return;

      // 如果目标内容为空，直接清空显示内容
      if (!targetContent) {
        if (msg.typingTimer) {
          clearInterval(msg.typingTimer);
          msg.typingTimer = null;
        }
        msg.displayedAnswerContent = "";
        return;
      }

      // 初始化显示内容
      if (!msg.displayedAnswerContent) {
        msg.displayedAnswerContent = "";
      }

      // 获取当前显示内容的长度
      const currentLength = msg.displayedAnswerContent.length;
      const targetLength = targetContent.length;

      // 如果显示内容已经等于或超过目标内容，不需要打字效果
      if (currentLength >= targetLength) {
        // 如果目标内容变短了（可能是内容被替换），直接更新
        if (targetLength < currentLength) {
          if (msg.typingTimer) {
            clearInterval(msg.typingTimer);
            msg.typingTimer = null;
          }
          msg.displayedAnswerContent = targetContent;
        }
        return;
      }

      // 如果已经有定时器在运行，不需要重新创建
      // 定时器会自动检查最新的 answerContent 并逐字显示
      if (msg.typingTimer) {
        return;
      }

      // 逐字显示
      const typingSpeed = 20; // 每个字符的延迟（毫秒）

      msg.typingTimer = setInterval(() => {
        // 重新获取最新的目标内容（因为流式数据可能还在更新）
        const latestTarget = msg.answerContent || targetContent || "";
        const latestCurrentLength = msg.displayedAnswerContent ? msg.displayedAnswerContent.length : 0;
        const latestTargetLength = latestTarget.length;

        // 如果目标内容已经完成，直接显示全部
        if (latestCurrentLength >= latestTargetLength) {
          msg.displayedAnswerContent = latestTarget;
          if (msg.typingTimer) {
            clearInterval(msg.typingTimer);
            msg.typingTimer = null;
          }
          return;
        }

        // 显示下一个字符
        msg.displayedAnswerContent = latestTarget.substring(0, latestCurrentLength + 1);

        // 滚动到底部
        this.$nextTick(() => {
          this.scrollToBottom();
        });
      }, typingSpeed);
    },

    /**
     * 格式化文本内容（支持 Markdown）
     * @param {string} text - 文本内容
     * @param {string} type - 内容类型（main/think）
     * @param {number} thinkIndex - 思考块索引
     */
    formatTextContent(text, type = "main", thinkIndex = null) {
      if (!text) return "";

      // 如果是思考内容，先清理标签
      if (type === "think" || thinkIndex !== null) {
        text = this.cleanThinkingTags(text);
      }

      // 先处理代码块和文件链接（在转义前处理）
      const codeBlockPlaceholders = [];
      const echartsPlaceholders = [];
      const fileLinkPlaceholders = [];
      
      // 处理文件链接（Markdown 格式：[文件名](/files/...)
      // 先保存普通链接，稍后处理
      const linkPlaceholders = [];
      let formatted = text.replace(
        /\[([^\]]+)\]\(([^)]+)\)/g,
        (match, linkText, url) => {
          // 检查是否是文件链接（URL 以 /files/ 开头）
          if (url && url.startsWith('/files/')) {
            const index = fileLinkPlaceholders.length;
            fileLinkPlaceholders.push({
              fileName: linkText,
              url: url
            });
            return `__FILE_LINK_${index}__`;
          }
          // 普通链接，保存为占位符
          const linkIndex = linkPlaceholders.length;
          linkPlaceholders.push({
            text: linkText,
            url: url
          });
          return `__LINK_${linkIndex}__`;
        }
      );
      
      // 处理代码块，包括 echarts
      formatted = formatted.replace(
        /```(\w+)?\n?([\s\S]*?)```/g,
        (match, lang, code) => {
          // 检查是否为 echarts 代码块
          if (lang && lang.toLowerCase() === "echarts") {
            const index = echartsPlaceholders.length;
            const chartId = `echarts-${Date.now()}-${index}`;
            try {
              // 尝试解析 JSON 配置
              const config = JSON.parse(code.trim());
              echartsPlaceholders.push({
                id: chartId,
                config: config
              });
              return `__ECHARTS_PLACEHOLDER_${index}__`;
            } catch (e) {
              // 如果解析失败，当作普通代码块处理
              console.warn("ECharts 配置解析失败:", e);
              const index = codeBlockPlaceholders.length;
              codeBlockPlaceholders.push(code.trim());
              return `__CODE_BLOCK_${index}__`;
            }
          } else {
            // 普通代码块
            const index = codeBlockPlaceholders.length;
            codeBlockPlaceholders.push(code.trim());
            return `__CODE_BLOCK_${index}__`;
          }
        }
      );

      // 转义 HTML
      formatted = formatted
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;");

      // 恢复 echarts 图表容器
      echartsPlaceholders.forEach((chart, index) => {
        // 将配置转换为 JSON 字符串并转义，以便存储在 HTML 属性中
        const configJson = JSON.stringify(chart.config)
          .replace(/&/g, "&amp;")
          .replace(/"/g, "&quot;")
          .replace(/'/g, "&#39;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");
        const chartContainer = `<div class="echarts-container" id="${chart.id}" style="width: 400px !important; height: 400px !important; min-width: 400px; min-height: 400px; margin: 20px 0; display: block; position: relative; box-sizing: border-box;" data-echarts-config="${configJson}"></div>`;
        formatted = formatted.replace(`__ECHARTS_PLACEHOLDER_${index}__`, chartContainer);
      });

      // 恢复代码块
      codeBlockPlaceholders.forEach((code, index) => {
        const escapedCode = code
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");
        formatted = formatted.replace(
          `__CODE_BLOCK_${index}__`,
          `<pre class="code-block"><code>${escapedCode}</code></pre>`
        );
      });

      // 恢复文件链接
      fileLinkPlaceholders.forEach((fileLink, index) => {
        const fileIcon = this.getFileIcon(fileLink.fileName);
        const escapedFileName = fileLink.fileName
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");
        const escapedUrl = fileLink.url
          .replace(/&/g, "&amp;")
          .replace(/"/g, "&quot;");
        const fileLinkHtml = `<div class="file-link-wrapper" onclick="window.downloadFile('${escapedUrl}', '${escapedFileName.replace(/'/g, "\\'")}')">
          <i class="${fileIcon}"></i>
          <span class="file-name">${escapedFileName}</span>
          <i class="el-icon-download download-icon" title="下载"></i>
        </div>`;
        formatted = formatted.replace(`__FILE_LINK_${index}__`, fileLinkHtml);
      });

      // 恢复普通链接
      linkPlaceholders.forEach((link, index) => {
        const escapedText = link.text
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");
        const escapedUrl = link.url
          .replace(/&/g, "&amp;")
          .replace(/"/g, "&quot;");
        const linkHtml = `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
        formatted = formatted.replace(`__LINK_${index}__`, linkHtml);
      });

      // 处理行内代码
      formatted = formatted.replace(
        /`([^`]+)`/g,
        '<code class="inline-code">$1</code>'
      );

      // 处理粗体
      formatted = formatted.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");

      // 处理换行
      formatted = formatted.replace(/\n/g, "<br>");

      return formatted;
    },

    // 格式化消息内容
    formatMessage(content) {
      if (!content) return "";
      // 简单的markdown转换
      return content
        .replace(/\n/g, "<br>")
        .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
        .replace(/\*(.*?)\*/g, "<em>$1</em>");
    },

    // 获取建议问题图标
    getSuggestionIcon(idx) {
      const icons = ["fas fa-passport", "fas fa-briefcase", "fas fa-id-card"];
      return icons[idx] || "fas fa-question-circle";
    },

    // 处理键盘事件
    handleKeydown(e) {
      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();
        this.sendMessage();
      }
    },

    // 滚动到底部
    scrollToBottom() {
      this.$nextTick(() => {
        if (this.$refs.chatMessages) {
          this.$refs.chatMessages.scrollTop =
            this.$refs.chatMessages.scrollHeight;
        }
      });
    },

    // 文件上传相关
    triggerFileUpload() {
      this.$refs.fileInput.click();
    },

    handleFileSelect(e) {
      const files = Array.from(e.target.files);
      files.forEach((file) => {
        this.attachments.push(file);
      });
      e.target.value = "";
    },

    removeAttachment(index) {
      this.attachments.splice(index, 1);
    },

    // 获取文件类型
    getFileType(filename) {
      const ext = (filename.split(".").pop() || "").toLowerCase();
      const imageExts = ["jpg", "jpeg", "png", "gif", "webp", "svg"];
      const docExts = [
        "txt",
        "md",
        "pdf",
        "doc",
        "docx",
        "html",
        "xlsx",
        "xls",
        "csv",
        "ppt",
        "pptx",
      ];
      if (imageExts.includes(ext)) {
        return "image";
      } else if (docExts.includes(ext)) {
        return "document";
      }
      return "document"; // 默认类型
    },

    // 获取文件图标
    getFileIcon(filename) {
      const ext = (filename.split(".").pop() || "").toLowerCase();
      const iconMap = {
        // Excel
        xlsx: "el-icon-document",
        xls: "el-icon-document",
        // Word
        doc: "el-icon-document",
        docx: "el-icon-document",
        // PDF
        pdf: "el-icon-document",
        // PowerPoint
        ppt: "el-icon-document",
        pptx: "el-icon-document",
        // 文本
        txt: "el-icon-document",
        md: "el-icon-document",
        // 图片
        jpg: "el-icon-picture",
        jpeg: "el-icon-picture",
        png: "el-icon-picture",
        gif: "el-icon-picture",
        webp: "el-icon-picture",
        svg: "el-icon-picture",
        // CSV
        csv: "el-icon-document",
        // 其他
        html: "el-icon-document",
      };
      return iconMap[ext] || "el-icon-document";
    },

    // 从上传结果中提取 Dify 文件 ID
    extractDifyFileId(uploadResult) {
      try {
        console.log("🔍 [调试] 完整的上传结果:", uploadResult);
        
        // uploadResult 是 ResponseResult 格式：{ code: 200, data: AiFile }
        const fileData = uploadResult?.data || uploadResult;
        console.log("🔍 [调试] 文件数据:", fileData);
        
        // 从 difyResponse 中解析 Dify 文件 ID
        if (fileData?.difyResponse) {
          const difyData = typeof fileData.difyResponse === 'string' 
            ? JSON.parse(fileData.difyResponse) 
            : fileData.difyResponse;
          const fileId = difyData?.id || difyData?.file_id;
          if (fileId) {
            console.log("✅ 从 difyResponse 中提取到文件 ID:", fileId);
            return fileId;
          }
        }
        
        // 尝试直接从 data 中获取
        if (fileData?.id) {
          console.log("✅ 从 data.id 中提取到文件 ID:", fileData.id);
          return fileData.id;
        }
        
        if (fileData?.file_id) {
          console.log("✅ 从 data.file_id 中提取到文件 ID:", fileData.file_id);
          return fileData.file_id;
        }
        
        // 如果 uploadResult 本身就是 id
        if (typeof uploadResult === 'string') {
          console.log("✅ uploadResult 本身就是文件 ID:", uploadResult);
          return uploadResult;
        }
        
        console.warn("⚠️ 未能提取到文件 ID，uploadResult:", uploadResult);
        return null;
      } catch (error) {
        console.error("❌ 提取文件 ID 时出错:", error);
        return null;
      }
    },

    handleLogin() {
      // 登录功能（可选）
      this.$router.push("/login");
    },

    /**
     * 初始化单个 echarts 图表
     * @param {string} chartId - 图表容器 ID
     * @param {Object} config - echarts 配置对象
     */
    initEchartsChart(chartId, config) {
      try {
        const chartDom = document.getElementById(chartId);
        if (!chartDom) {
          console.warn(`图表容器 ${chartId} 不存在`);
          return;
        }

        // 检查容器是否有尺寸，如果没有则延迟初始化
        let retryCount = 0;
        const maxRetries = 20; // 最多重试 20 次（2秒）
        const checkAndInit = () => {
          const rect = chartDom.getBoundingClientRect();
          const width = rect.width || chartDom.offsetWidth || chartDom.clientWidth;
          const height = rect.height || chartDom.offsetHeight || chartDom.clientHeight;

          // 如果容器尺寸为 0，延迟初始化
          if ((width === 0 || height === 0) && retryCount < maxRetries) {
            retryCount++;
            console.warn(`图表容器 ${chartId} 尺寸为 0，延迟初始化 (${retryCount}/${maxRetries})`);
            setTimeout(checkAndInit, 100);
            return;
          }

          // 如果超过最大重试次数，使用默认尺寸
          const finalWidth = 400; // 强制宽度 400px
          const finalHeight = height > 0 ? height : 400; // 默认高度 400px
          
          if (width === 0 || height === 0) {
            console.warn(`图表容器 ${chartId} 尺寸仍为 0，使用默认尺寸 ${finalWidth}x${finalHeight}`);
          }

          // 如果已经存在实例，先销毁
          if (this.echartsInstances.has(chartId)) {
            const oldChart = this.echartsInstances.get(chartId);
            if (oldChart && typeof oldChart.dispose === "function") {
              oldChart.dispose();
            }
          }

          // 确保容器有明确的尺寸
          chartDom.style.width = finalWidth + 'px';
          chartDom.style.height = finalHeight + 'px';
          chartDom.style.display = 'block';
          chartDom.style.position = 'relative';
          chartDom.style.boxSizing = 'border-box';

          // 创建新的 echarts 实例
          const myChart = echarts.init(chartDom);
          
          // 设置配置项
          myChart.setOption(config);

          // 立即调整大小，确保图表正确显示
          // 使用多个 setTimeout 确保在不同时机都能正确调整
          setTimeout(() => {
            if (myChart && typeof myChart.resize === "function") {
              myChart.resize();
            }
          }, 0);
          
          setTimeout(() => {
            if (myChart && typeof myChart.resize === "function") {
              myChart.resize();
            }
          }, 100);
          
          setTimeout(() => {
            if (myChart && typeof myChart.resize === "function") {
              myChart.resize();
            }
          }, 300);

          // 存储实例
          this.echartsInstances.set(chartId, myChart);

          // 响应式调整大小
          const resizeHandler = () => {
            if (myChart && typeof myChart.resize === "function") {
              myChart.resize();
            }
          };
          window.addEventListener("resize", resizeHandler);

          // 存储 resize 处理器，以便后续清理
          myChart._resizeHandler = resizeHandler;

          console.log(`✅ ECharts 图表 ${chartId} 初始化成功，尺寸: ${finalWidth}x${finalHeight}`);
        };

        // 立即尝试初始化，如果失败则延迟
        checkAndInit();
      } catch (error) {
        console.error(`❌ ECharts 图表 ${chartId} 初始化失败:`, error);
      }
    },

    /**
     * 初始化所有未初始化的 echarts 图表
     */
    initAllEchartsCharts() {
      // 查找所有 echarts 容器
      const chartContainers = document.querySelectorAll(".echarts-container");
      chartContainers.forEach((container) => {
        const chartId = container.id;
        if (!chartId) return;

        // 如果已经初始化过，跳过
        if (this.echartsInstances.has(chartId)) {
          return;
        }

        // 从 data-echarts-config 属性中获取配置
        const configStr = container.getAttribute("data-echarts-config");
        if (!configStr) {
          console.warn(`图表容器 ${chartId} 缺少配置`);
          return;
        }

        try {
          // 反转义 HTML 实体
          const unescapedConfig = configStr
            .replace(/&quot;/g, '"')
            .replace(/&#39;/g, "'")
            .replace(/&lt;/g, "<")
            .replace(/&gt;/g, ">")
            .replace(/&amp;/g, "&");
          const config = JSON.parse(unescapedConfig);
          this.initEchartsChart(chartId, config);
        } catch (error) {
          console.error(`解析图表配置失败 ${chartId}:`, error);
        }
      });
    },
  },
};
</script>

<style>
/* 引入 Font Awesome 图标库（本地文件） */
@import url('/fontawesome/css/all.min.css');
</style>

<style scoped>
/* 全局样式重置 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.page-wrapper {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 增强玻璃拟态浮动元素 */
.glass-float {
  position: fixed;
  border-radius: 50%;
  filter: blur(80px);
  opacity: 0.35;
  z-index: 0;
  animation: float 20s infinite alternate ease-in-out;
}

@keyframes float {
  0% {
    transform: translate(0, 0) scale(1);
  }
  100% {
    transform: translate(50px, 30px) scale(1.1);
  }
}

.float-1 {
  width: 320px;
  height: 320px;
  background: linear-gradient(
    135deg,
    rgba(100, 100, 100, 0.3),
    rgba(150, 150, 150, 0.2)
  );
  top: 15%;
  left: 15%;
}

.float-2 {
  width: 240px;
  height: 240px;
  background: linear-gradient(
    135deg,
    rgba(120, 120, 120, 0.25),
    rgba(180, 180, 180, 0.15)
  );
  bottom: 15%;
  right: 15%;
}

.float-3 {
  width: 180px;
  height: 180px;
  background: linear-gradient(
    135deg,
    rgba(80, 80, 80, 0.2),
    rgba(130, 130, 130, 0.15)
  );
  top: 30%;
  right: 20%;
  animation-delay: -5s;
}

.float-4 {
  width: 150px;
  height: 150px;
  background: linear-gradient(
    135deg,
    rgba(90, 90, 90, 0.2),
    rgba(140, 140, 140, 0.12)
  );
  bottom: 25%;
  left: 25%;
  animation-delay: -10s;
}

/* 页面禁用遮罩样式 */
.page-disabled-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
}

.mask-content {
  background: #fff;
  border-radius: 8px;
  padding: 24px 28px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  text-align: center;
  max-width: 520px;
}

.mask-icon {
  font-size: 32px;
  color: #e6a23c;
}

.mask-title {
  margin-top: 8px;
  font-size: 18px;
  font-weight: 600;
  color: #303133;
}

.mask-desc {
  margin-top: 6px;
  color: #606266;
}



.home-root {
  position: relative;
  width: 100%;
  height: calc(100% - 66px); /* 减去导航栏高度 */
  margin-top: 66px; /* 为固定导航栏留出空间 */
  display: flex;
  flex-direction: row;
  background: linear-gradient(135deg, #f5f7ff 0%, #f0f4ff 100%);
  --c-primary: #3b82f6;
  --c-primary-2: #2563eb;
  --c-accent: #0ea5e9;
  --c-bg: #f1f5f9;
  --c-card: #ffffff;
  --c-border: #cbd5e1;
  --c-muted: #64748b;
  --c-law-blue: #3b82f6;
  --c-law-dark: #2563eb;
  --c-law-gray: #475569;
}

/* 主内容区样式 */
.main-container {
  max-width: 1560px;
  width: 100%;
  margin: 20px auto;
  padding: 0 24px;
  display: flex;
  gap: 35px;
  position: relative;
  z-index: 1;
  height: 100%;
}

/* 左侧面板样式 */
.left-panel {
  flex: 2;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.panel-section {
  background: rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  border-radius: 24px;
  padding: 18px;
  border: 1px solid rgba(255, 255, 255, 0.75);
  transition: all 0.4s ease;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.12);
  height: 100%;
  display: flex;
  flex-direction: column;
}

.panel-section:hover {
  transform: translateY(-7px);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.18);
}

.section-title {
  font-size: 20px;
  color: #333;
  margin-bottom: 22px;
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  font-weight: 600;
  text-align: left;
  position: relative;
}

.section-title::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 45px;
  height: 3px;
  background: linear-gradient(
    to right,
    rgba(0, 0, 0, 0.8),
    rgba(100, 100, 100, 0.6)
  );
  border-radius: 3px;
}

.section-title i {
  margin-right: 12px;
  color: #666;
}

.services-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
  overflow-y: auto;
}

.service-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(6px);
  border-radius: 16px;
  padding: 12px 16px;
  cursor: pointer;
  transition: all 0.3s ease;
  border: 1px solid rgba(255, 255, 255, 0.8);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.06);
  color: #333;
  position: relative;
}

.service-item:hover {
  background: rgba(255, 255, 255, 1);
  transform: translateY(-6px);
  border-color: rgba(255, 255, 255, 0.9);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
}

.service-item.active {
  background: rgba(0, 0, 0, 0.05);
  border-color: rgba(0, 0, 0, 0.15);
}

.service-item i {
  font-size: 18px;
  color: #666;
  flex-shrink: 0;
}

.conversation-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
}

.delete-conv-btn {
  background: transparent;
  border: none;
  color: #999;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 4px;
  transition: all 0.2s;
  flex-shrink: 0;
}

.delete-conv-btn:hover {
  background: rgba(0, 0, 0, 0.1);
  color: #333;
}

.new-chat-btn {
  background: linear-gradient(135deg, rgba(0, 0, 0, 0.05), rgba(100, 100, 100, 0.02));
  border: 2px dashed rgba(0, 0, 0, 0.2);
}

.new-chat-btn:hover {
  background: linear-gradient(135deg, rgba(0, 0, 0, 0.1), rgba(100, 100, 100, 0.05));
  border-color: rgba(0, 0, 0, 0.3);
}

/* 聊天区域样式 */
.chat-container {
  flex: 6;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(14px) saturate(130%);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  border-radius: 24px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.1);
}

.chat-messages {
  flex: 1;
  padding: 28px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 32px;
  background: rgba(245, 245, 245, 0.5);
}

.message {
  display: flex;
  gap: 18px;
  animation: fadeIn 0.6s ease;
}

.message.user-message {
  align-items: flex-start;
  justify-content: flex-end;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(15px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.message-avatar {
  flex-shrink: 0;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 20px;
  background: rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);
}

.ai-avatar-small {
  width: 60px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  background: transparent;
}

.ai-avatar-small img {
  width: 60px;
  height: 60px;
  object-fit: contain;
  background: transparent;
  mix-blend-mode: normal;
}

.user-avatar-small {
  background: linear-gradient(135deg, #3b82f6, #2563eb);
}

.user-avatar-text {
  font-size: 20px;
  font-weight: 600;
  color: white;
  user-select: none;
}

.header-bubble {
  padding: 20px 25px;
  border-radius: 24px;
  max-width: 90%;
  margin: 0 auto;
  transition: all 0.3s ease;
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06);
}

.bubble-container {
  display: flex;
  gap: 20px;
  align-items: flex-start;
}

.gif-container {
  flex-shrink: 0;
  width: 20%;
  margin-left: -5%;
}

.gif-container img {
  width: 100%;
  height: 220px;
  border-radius: 10px;
}

.message-bubble {
  padding: 20px 25px;
  border-radius: 24px;
  max-width: 86%;
  transition: all 0.3s ease;
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.06);
}

.welcome-content {
  margin-top: 5%;
  line-height: 1.75;
  color: #333;
  max-width: 800px;
  margin-left: auto;
  margin-right: auto;
}

.welcome-content strong {
  color: #333;
  font-weight: 600;
}

.welcome-title {
  font-size: 1.3em;
  text-align: left;
  margin-bottom: 15px;
  color: #1976d2;
  font-weight: 700;
  letter-spacing: 1px;
}

.welcome-txt {
  line-height: 2em;
  text-indent: 2em;
}

.user-bubble {
  background: rgba(255, 255, 255, 0.85);
  margin-left: 0;
  align-self: flex-end;
  border-top-right-radius: 8px;
  border-top-left-radius: 24px;
}

.message-content {
  line-height: 1.75;
  color: #333;
}

.message-text {
  line-height: 1.75;
  color: #333;
}

/* 思考过程和回答内容的样式优化 */
.message-text ::v-deep .thinking-section {
  margin-bottom: 24px;
  padding: 16px;
  background: #f8fafc;
  border-radius: 12px;
  border-left: 4px solid #9ca3af;
  font-family: "Segoe UI", "Microsoft YaHei UI", sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: #4b5563;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}

.message-text ::v-deep .answer-section {
  font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans", sans-serif;
  font-size: 16px;
  line-height: 1.8;
  color: #1f2937;
  font-weight: 400;
}

/* ECharts 图表容器样式 */
.message-text ::v-deep .echarts-container {
  width: 400px !important;
  height: 400px !important;
  min-width: 400px !important;
  min-height: 400px;
  max-width: 400px !important;
  margin: 20px 0;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
  background: #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  padding: 16px;
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  display: block;
}

.message-text ::v-deep .answer-section .echarts-container {
  background: #ffffff;
  border: 1px solid #e5e7eb;
}

.message-text ::v-deep .thinking-section .echarts-container {
  background: #f9fafb;
  border: 1px solid #e5e7eb;
}

/* 确保 echarts canvas 有正确的尺寸 */
.message-text ::v-deep .echarts-container canvas {
  width: 100% !important;
  height: 100% !important;
}

/* 文件链接样式 */
.message-text ::v-deep .file-link-wrapper {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  margin: 8px 4px;
  background: #f0f7ff;
  border: 1px solid #b3d8ff;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.2s ease;
  text-decoration: none;
  color: #1976d2;
  font-size: 14px;
  max-width: 100%;
  box-sizing: border-box;
}

.message-text ::v-deep .file-link-wrapper:hover {
  background: #e3f2fd;
  border-color: #90caf9;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.message-text ::v-deep .file-link-wrapper i:first-child {
  font-size: 18px;
  color: #1976d2;
  flex-shrink: 0;
}

.message-text ::v-deep .file-link-wrapper .file-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #1976d2;
  font-weight: 500;
}

.message-text ::v-deep .file-link-wrapper .download-icon {
  font-size: 16px;
  color: #1976d2;
  opacity: 0.7;
  flex-shrink: 0;
  transition: opacity 0.2s ease;
}

.message-text ::v-deep .file-link-wrapper:hover .download-icon {
  opacity: 1;
}

/* 思考过程标题样式 */
.message-text ::v-deep .thinking-section .think-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid #e5e7eb;
  font-size: 13px;
  font-weight: 600;
  color: #6b7280;
  cursor: pointer;
  transition: all 0.2s ease;
}

.message-text ::v-deep .thinking-section .think-header:hover {
  color: #4b5563;
  background: rgba(0, 0, 0, 0.02);
  border-radius: 6px;
  padding: 4px 8px;
  margin: -4px -8px 8px -8px;
}

.message-text ::v-deep .thinking-section .think-header i:first-child {
  font-size: 16px;
  color: #9ca3af;
}

.message-text ::v-deep .thinking-section .think-toggle-icon {
  font-size: 14px;
  color: #9ca3af;
  transition: transform 0.2s ease;
  margin-left: auto;
}

/* 思考内容样式 */
.message-text ::v-deep .thinking-section .think-content {
  font-size: 16px;
  line-height: 1.6;
  color: #6b7280;
  transition: all 0.3s ease;
  overflow: hidden;
  animation: fadeInUp 0.3s ease-out;
}

.message-text ::v-deep .thinking-section .think-content.collapsed {
  max-height: 0;
  opacity: 0;
  margin-bottom: 0;
}

.message-text ::v-deep .thinking-section .think-content:not(.collapsed) {
  max-height: 1000px;
  opacity: 1;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.suggestions-title {
  font-size: 15px;
  color: #4f5050;
  margin-top: 16px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}

.suggestions-title i {
  color: #fbbf24;
  font-size: 16px;
}

.suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 18px;
}

.suggestion {
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 14px;
  padding: 12px 20px;
  font-size: 15px;
  cursor: pointer;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  gap: 10px;
  color: #333;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
}

.suggestion:hover {
  background: rgba(255, 255, 255, 1);
  transform: translateY(-4px);
  border-color: rgba(0, 0, 0, 0.15);
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.1);
  color: #333;
}

.suggestion i {
  color: #666;
  transition: all 0.3s ease;
}

.suggestion .fa-passport {
  color: #3b82f6;
}

.suggestion .fa-briefcase {
  color: #10b981;
}

.suggestion:nth-child(3) i {
  color: #999;
}

.typing-indicator {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #666;
  font-style: italic;
}

.typing-dots {
  display: flex;
  gap: 4px;
}

.dot {
  width: 9px;
  height: 9px;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  animation: bounce 1.5s infinite;
}

.dot:nth-child(2) {
  animation-delay: 0.2s;
}

.dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes bounce {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-7px);
  }
}

.input-area {
  padding: 22px;
  border-top: 1px solid rgba(0, 0, 0, 0.08);
  background: rgba(255, 255, 255, 0.65);
  backdrop-filter: blur(8px);
  position: relative;
}


.attachments-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}

.uploaded-files {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
}

.uploaded-file-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: rgba(255, 255, 255, 0.5);
  border-radius: 4px;
  font-size: 13px;
  color: #666;
}

.uploaded-file-item i {
  color: #4a90e2;
}

.attachment-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 8px;
  font-size: 13px;
  border: 1px solid rgba(0, 0, 0, 0.1);
}

.attachment-item i {
  color: #666;
}

.remove-file-btn {
  background: transparent;
  border: none;
  color: #999;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
  transition: all 0.2s;
}

.remove-file-btn:hover {
  background: rgba(255, 0, 0, 0.1);
  color: #ff4444;
}

.input-box {
  position: relative;
}

.input-box textarea {
  width: 100%;
  height: 90px;
  resize: none;
  padding: 20px 150px 20px 28px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 18px;
  font-size: 16px;
  transition: all 0.3s;
  background: rgba(255, 255, 255, 0.85);
  color: #333;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
  backdrop-filter: blur(4px);
  font-family: inherit;
}

.input-box textarea:focus {
  outline: none;
  border-color: rgba(13, 71, 161, 0.6);
  box-shadow: 0 0 0 4px rgba(13, 71, 161, 0.15);
}

.input-box textarea::placeholder {
  color: #999;
}

.upload-btn {
  position: absolute;
  right: 90px;
  bottom: 18px;
  width: 48px;
  height: 48px;
  background: rgba(255, 255, 255, 0.9);
  color: #666;
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  transition: all 0.3s ease;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

.upload-btn:hover {
  background: rgba(255, 255, 255, 1);
  transform: scale(1.05);
  box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
  color: #333;
}

.send-btn {
  position: absolute;
  right: 18px;
  bottom: 18px;
  width: 58px;
  height: 58px;
  background: linear-gradient(135deg, #3b82f6, #2563eb);
  color: white;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  transition: all 0.3s ease;
  box-shadow: 0 6px 12px rgba(59, 130, 246, 0.3);
}

.send-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #2563eb, #1d4ed8);
  transform: scale(1.08) rotate(5deg);
  box-shadow: 0 8px 18px rgba(59, 130, 246, 0.4);
}

.send-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.hidden-file {
  display: none;
}

.glass-decoration {
  position: fixed;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, transparent 70%);
  filter: blur(30px);
  z-index: 0;
}

.decoration-1 {
  top: 20%;
  right: 15%;
}

.decoration-2 {
  bottom: 25%;
  left: 20%;
}

/* 滚动条样式 */
::-webkit-scrollbar {
  width: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.2);
  border-radius: 5px;
}

::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 0, 0, 0.3);
}

/* 响应式设计 */
@media (max-height: 1080px) {
  .main-container {
    margin: 20px auto;
    gap: 25px;
  }

  .chat-messages {
    padding: 18px;
    gap: 24px;
  }
}

@media (max-width: 992px) {
  .main-container {
    flex-direction: column;
  }

  .chat-container,
  .left-panel {
    height: auto;
  }
}
</style>


